#include "jeux.h"
#include "plateau.h"
#include <iostream>
#include <fstream>
#include <limits>
#include <cstdlib>

using namespace std ;

/**
  * @brief Constructeur du jeu avec choix des paramètres de la partie par les joueurs.
  */
Jeux::Jeux()
{
    char load ;
    fstream file("save", ios::in) ;
    if ( file )
    {
        do
        {
            cout << "Voulez-vous charger une partie sauvegardee ? o/n : " ;
            cin >> load ;
            if ( load != 'o' && load != 'n' ) cout << "Vous devez entrer o ou n..." << endl ;
        } while ( load != 'o' && load != 'n' ) ;
    }
    if ( load == 'o' )
    {
        charger() ;
    }
    else
    {
        int largeur ;
        int hauteur ;
        do
        {
            cout << "Entrez une largeur : " ;
            cin >> largeur ;
            cin.clear() ;
            cin.ignore(numeric_limits<int>::max(), '\n') ;
            if ( largeur < 2 ) cout << "Il faut au minimum une largeur de 2." << endl ;
            if ( largeur > 45 ) cout << "Pour des raisons d'affichages, la largeur maximale est 45." << endl ;
        } while ( largeur < 2 || largeur > 45 ) ;
        do
        {
            cout << "Entrez une hauteur : " ;
            cin >> hauteur ;
            cin.clear() ;
            cin.ignore(numeric_limits<int>::max(), '\n');
            if ( hauteur < 2 ) cout << "Il faut au minimum une hauteur de 2." << endl ;
            if ( hauteur > 15 ) cout << "Pour des raisons d'affichages, la hauteur maximale est 15." << endl ;
        } while ( hauteur < 2 || hauteur > 15 ) ;
        Plateau plateau(largeur,hauteur,false) ;
        cout << "1 ou 2 joueurs ? " ;
        do
        {
            cin >> _nbJoueurs ;
            cin.clear() ;
            cin.ignore(numeric_limits<int>::max(), '\n');
            if ( _nbJoueurs != 1 && _nbJoueurs != 2 )
            {
                cout << "Il faut entrer une valeur correcte qui soit 1 ou 2 ... : " ;
                cin >> _nbJoueurs ;
            }
        } while ( _nbJoueurs != 1 && _nbJoueurs != 2 ) ;
        cout << "Joueur 1, entrez votre nom : " ;
        cin >> _joueur1 ;
        if ( _nbJoueurs == 2 )
        {
            cout << "Joueur 2, entrez votre nom : " ;
            cin >> _joueur2 ;
            while ( _joueur2 == _joueur1 )
            {
                cout << "Ce nom est deja pris, choisissez en un autre : " ;
                cin >> _joueur2 ;
                cout << endl ;
            }
            _difficulteIA = 0 ;
        }
        else
        {
            _joueur2 = "Ordinateur" ;
            cout << "Chosissez le niveau de l'ordinateur (1 = tres facile, 2 = moyen, 3 = tres difficile)" << endl ;
            do
            {
                cin >> _difficulteIA ;
                cin.clear() ;
                cin.ignore(numeric_limits<int>::max(), '\n');
                if ( _difficulteIA < 1 || _difficulteIA > 3 )
                {
                    cout << "Il faut entrer une valeur correcte qui comprise entre 1 et 3... : " ;
                    cin >> _difficulteIA ;
                }
            } while ( _difficulteIA < 1 || _difficulteIA > 3 ) ;
            if ( _difficulteIA == 3 ) _difficulteIA = 4 ;
        }
        do
        {
            cout << "Entrez le nombre de manches pour gagner la partie : " ;
            cin >> _nbManches ;
            cin.clear() ;
            cin.ignore(numeric_limits<int>::max(), '\n');
            if ( _nbManches < 1 ) cout << "Il faut au minimum 1 manche pour pouvoir jouer." << endl ;
        } while ( _nbManches < 1 ) ;
        jouerPartie(plateau) ;
    }
}



/**
  * @brief Affichage des règles du jeu
  */
void Jeux::regles()
{
    cout << "Voici les regles du jeu :" << endl ;
    cout << "Ce jeu est un derive du Puissance4" << endl ;
    cout << "Dans un plateau de jeu dont les dimensions sont determinees par le joueur, l'objectif est d'aligner un nombre de pions choisi," << endl ;
    cout << "tout en empechant l'adversaire d'en faire de meme." << endl ;
    cout << "Pour ce faire il est possible de : " << endl ;
    cout << "   - Placer un pion dans une colonne, ce pion tombera le plus bas possible dans la colonne." << endl ;
    cout << "   - Supprimer un pion dans une colonne, il est interdit de supprimmer le dernier pion pose ou un equivalent." << endl ;
    cout << "   - Faire tourner le plateau d'un quart de tour vers la droite ou la gauche." << endl ;
    cout << "Chaque partie est composee d'une ou plusieurs manches." << endl ;
    cout << "Il est possible de jouer soit contre un humain, soit contre l'ordinateur, auquel cas plusieurs niveaux de difficulte sont proposes." << endl ;
    cout << endl ;
}



/**
  * @brief Affichage des paramètres de la partie et de la manche
  */
void Jeux::rappelParametres()
{
    cout << "Vous jouez une partie avec " << _nbJoueurs << " joueur(s)." << endl ;
    cout << "Joueur 1 : " << _joueur1 << endl ;
    if ( _nbJoueurs == 2 ) cout << "Joueur 2 : " << _joueur2 << endl ;
    else cout << "L'ordinateur est en difficulte " << _difficulteIA << endl ;
    cout << "Score :" << endl ;
    cout << _joueur1 << " : " << _mancheGagneeJoueur1 << " - " << _joueur2 << " : " << _mancheGagneeJoueur2 << endl << endl ;
    cout << "Il faut faire " << _alignement << " alignements pour gagner une manche." << endl ;
    cout << "Cette partie se joue en " << _nbManches << " manche(s)." << endl ;
}



/**
  * @brief Déroulement de la partie
  * @param plateau Le plateau de jeu
  * @param load True si la partie a été restaurée, false sinon
  */
void Jeux::jouerPartie(Plateau plateau, bool load)
{
    _joueur = 1 ;
    _mancheGagneeJoueur1 = 0 ;
    _mancheGagneeJoueur2 = 0 ;
    do
    {
        if ( !load )
        {
            int alignementMax ;
            if ( plateau.getLargeur() < plateau.getHauteur() ) alignementMax = plateau.getHauteur() ;
            else alignementMax = plateau.getLargeur() ;
            do
            {
                cout << "Entrez le nombre d'alignement necessaire pour gagner : " ;
                cin >> _alignement ;
                cin.clear() ;
                cin.ignore(numeric_limits<int>::max(), '\n');
                if ( _alignement < 2 ) cout << "Il faut au minimum aligner 2 pions pour gagner." << endl ;
                if ( _alignement > alignementMax ) cout << "L'alignement maximal est plus grand que la largeur et la hauteur du plateau." << endl ;
            } while ( _alignement < 2 || _alignement > alignementMax ) ;
        }
        else
        {
            rappelParametres() ;
        }
        cout << endl ;
        _mancheFinie = false ;
        load = false ;
        do
        {
            plateau.affichage() ;
            choisirAction(plateau) ;
            if ( plateau.verif(_alignement) == 'X' )
            {
                plateau.affichage() ;
                cout << _joueur1 << " a fait le bon alignement, il gagne la manche." << endl ;
                _mancheGagneeJoueur1++ ;
                _mancheFinie = true ;
            }
            if ( plateau.verif(_alignement) == 'O' )
            {
                plateau.affichage() ;
                if ( _nbJoueurs == 2 ) cout << _joueur2 << " a fait le bon alignement, il gagne la manche." << endl ;
                else cout << "L'ordinateur a fait le bon alignement, il gagne la manche." << endl ;
                _mancheGagneeJoueur2++ ;
                _mancheFinie = true ;
            }
            if ( _joueur == 1 ) _joueur = 2 ;
            else _joueur = 1 ;
        } while ( !_mancheFinie ) ;
        cout << "Score :" << endl ;
        cout << _joueur1 << " : " << _mancheGagneeJoueur1 << " - " << _joueur2 << " : " << _mancheGagneeJoueur2 << endl << endl ;
        plateau.reinitialiser() ;
        if ( _mancheGagneeJoueur1 == _nbManches ) cout << "Felicitations " << _joueur1 << " ! Vous avez gagne la partie !" << endl << endl ;
        else if ( _mancheGagneeJoueur2 == _nbManches )
        {
            if ( _nbJoueurs == 2 ) cout << "Felicitations " << _joueur2 << " ! Vous avez gagne la partie !" << endl << endl ;
            else cout << "Dommage, vous avez perdu la partie..." << endl << endl ;
        }
    } while ( _mancheGagneeJoueur1 < _nbManches && _mancheGagneeJoueur2 < _nbManches ) ;
}



/**
  * @brief Permet de choisir une action parmis toutes celles disponibles
  * @param plateau Le plateau de jeu
  */
void Jeux::choisirAction(Plateau & plateau)
{
    bool actionEffectuee = false ;
    char action ;
    if ( _joueur == 1 || _nbJoueurs == 2 )
    do
    {
        do
        {
            if ( _joueur == 1 ) cout << endl << _joueur1 ;
            else cout << endl << _joueur2 ;
            cout << " : Choisisssez une action : " << endl << "a pour afficher le plateau de jeu." << endl ;
            cout << "j pour ajouter un pion." << endl << "s pour supprimer un pion." << endl ;
            cout << "0 pour abandonner la manche" << endl ;
            cout << "t pour tourner le plateau de jeu." << endl << "q pour quitter" << endl ;
            cout << "! pour sauvegarder la partie" << endl << "? pour afficher les regles du jeu" << endl << ": pour afficher les parametres de la partie" << endl ;
            cin >> action ;
            cout << endl ;
            if ( action != 'j' && action != 's' && action != 't' && action != 'q' && action != '0' && action != '!' && action != '?' && action !=':' && action != 'a') cout << "Action invalide !" << endl ;
            else if ( action == 'q' ) exit(0) ;
            else if ( action == '?' ) regles() ;
            else if ( action == '!' ) sauvegarder(plateau) ;
            else if ( action == ':' ) rappelParametres() ;
            else if ( action == 'a' ) plateau.affichage() ;
        } while ( action != 'j' && action != 's' && action != 't' && action != 'q' && action != '0' ) ;
        switch(action)
        {
        case 'j':
            if ( _joueur == 1 ) actionEffectuee = plateau.jouer(_joueur, _joueur1) ;
            else actionEffectuee = plateau.jouer(_joueur, _joueur2) ;
            break ;
        case 's':
            if ( _joueur == 1 ) actionEffectuee = plateau.supprimer(_joueur1) ;
            else actionEffectuee = plateau.supprimer(_joueur2) ;
            break ;
        case 't':
            if ( _joueur == 1 ) actionEffectuee = plateau.tourner(_joueur1) ;
            else actionEffectuee = plateau.tourner(_joueur2) ;
            break ;
        case '0':
            if ( _joueur == 1 )
            {
                cout << "Felicitations " << _joueur2 << " ! Vous avez gagne la manche !" << endl ;
                _mancheGagneeJoueur2++;
            }
            else
            {
                cout << "Felicitations " << _joueur1 << " ! Vous avez gagne la manche !" << endl ;
                _mancheGagneeJoueur1++ ;
            }
            _mancheFinie = true ;
            actionEffectuee = true ;
            break ;
        }
    } while ( !actionEffectuee ) ;
    else
    {
        plateau.jouerIa(plateau.evaluation(0,_difficulteIA,_alignement),_difficulteIA,_alignement) ;

        /// ESSAIS AVEC ALPHA BETA ///
        //int alpha = -1000000 ;
        //int beta = 1000000 ;
        //plateau.jouerIa(plateau.evaluation2(0,_difficulteIA,_alignement,alpha,beta),_difficulteIA,_alignement) ;
        if ( plateau.estPlein() )
        {
            plateau.affichage() ;
            cout << endl << "Le plateau de jeu est rempli sans qu'il n'y ait de gagnant, reinitialisation ..." << endl ;
            plateau.reinitialiser() ;
        }
    }
}



/**
  * @brief Sauvegarde la partie courante dans le fichier save
  * @param plateau Le plateau de jeu
  */
void Jeux::sauvegarder(Plateau plateau)
{
    ofstream file("save", ios::out | ios::trunc) ;
    if ( !file )
    {
        cout << "Le fichier n'a pas pu etre sauvegarde ..." << endl ;
    }
    else
    {
        file << _nbJoueurs << " " << _difficulteIA << " " << _joueur1 << " " << _joueur2 << " " << _joueur << " " << _mancheGagneeJoueur1 << " " << _mancheGagneeJoueur2 << " " << _nbManches << " " << _alignement << " " ;
    }
    file.close() ;
    plateau.sauvegarder() ;
    cout << "La partie a ete sauvegardee" << endl ;
}



/**
  * @brief Charge la dernière partie sauvegardée dans le fichier save
  */
void Jeux::charger()
{
    fstream file("save", ios::in) ;
    if ( !file )
    {
        cout << "Le fichier n'a pas pu etre ouvert ..." << endl ;
    }
    else
    {
        int largeur ;
        int hauteur ;
        file >> _nbJoueurs >> _difficulteIA >> _joueur1 >> _joueur2 >> _joueur >> _mancheGagneeJoueur1 >> _mancheGagneeJoueur2 >> _nbManches >> _alignement >> largeur >> hauteur ;
        Plateau plateau(largeur,hauteur,true) ;
        jouerPartie(plateau,true);
    }
    file.close() ;
}
